﻿using Autodesk.Revit.DB;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace demo.Extensions
{
	/// <summary>
	/// Revit中曲线计算Transform的结果符合右手坐标
	/// </summary>
	public static class TransformUtils
	{
		/// <summary>
		/// Z轴为曲线切线方向，Y轴为曲线平面法线方向，符合左手坐标
		/// </summary>
		/// <param name="curve"></param>
		/// <param name="para"></param>
		/// <returns></returns>
		public static Transform GetTransformVertical(Curve curve, double para)
		{
			Transform transform = Transform.Identity;
			if (curve is Line)
			{
				Line line = curve as Line;
				transform.Origin = line.Evaluate(para, true);
				transform.BasisZ = line.Direction;
				if (line.Direction.IsAlmostEqualTo(XYZ.BasisX))
				{
					transform.BasisX = XYZ.BasisY.Negate();
					transform.BasisY = XYZ.BasisZ;
				}
				else if (line.Direction.IsAlmostEqualTo(XYZ.BasisX.Negate()))
				{
					transform.BasisX = XYZ.BasisY;
					transform.BasisY = XYZ.BasisZ;
				}
				else
				{
					var basisy = XYZ.BasisX.CrossProduct(line.Direction).Normalize();
					transform.BasisY = basisy;
					transform.BasisX = line.Direction.CrossProduct(basisy).Normalize();
				}
			}
			else
			{
				Transform trans = curve.ComputeDerivatives(para, true);
				transform.Origin = trans.Origin;
				transform.BasisZ = trans.BasisX.Normalize();
				transform.BasisX = trans.BasisZ.Normalize();
				transform.BasisY = trans.BasisX.CrossProduct(trans.BasisZ).Normalize();//避免误差导致的错误
			}
			return transform;
		}
		/// <summary>
		/// XY平面为曲线平面
		/// </summary>
		/// <param name="curve"></param>
		/// <param name="para"></param>
		/// <returns></returns>
		public static Transform GetTransformParallel(Curve curve, double para)
		{
			Transform transform = Transform.Identity;
			if (curve is Line)
			{
				Line line = curve as Line;
				XYZ yAxis = XYZ.BasisZ.CrossProduct(line.Direction).Normalize();
				transform.BasisX = line.Direction;
				transform.BasisY = yAxis;
				transform.BasisZ = line.Direction.CrossProduct(yAxis).Normalize();
			}
			else
			{
				Transform trans = curve.ComputeDerivatives(para, true);
				transform.BasisX = trans.BasisX.Normalize();
				transform.BasisY = trans.BasisY.Normalize();
				transform.BasisZ = trans.BasisZ.Normalize();
				transform.Origin = trans.Origin;
			}
			return transform;
		}
		/// <summary>
		/// XY平面垂直于水平面，直线时方向不能为XYZ.BasisZ
		/// </summary>
		/// <param name="path"></param>
		/// <param name="para"></param>
		/// <returns></returns>
		public static Transform GetTransform(Curve path, double para)
		{
			XYZ origin, normal;
			if (path is Line)
			{
				Line line = path as Line;
				origin = line.Evaluate(para, true);//计算并返回沿着曲线与参数匹配的点。
				normal = new XYZ(line.Direction.X, line.Direction.Y, 0);
			}
			else
			{
				Transform transform = path.ComputeDerivatives(para, normalized: true);
				origin = transform.Origin;
				normal = new XYZ(transform.BasisX.X, transform.BasisY.Y, 0);
			}
			return GetTransform(origin, normal);
		}


		/// <summary>
		/// normal为XY平面向量,结果XY平面垂直于normal,Y轴为XYZ.BasisZ，符合左手坐标
		/// </summary>
		/// <param name="origin"></param>
		/// <param name="normal"></param>
		/// <returns></returns>
		public static Transform GetTransform(XYZ origin, XYZ normal)
		{
			XYZ zNormal = new XYZ(normal.X, normal.Y, 0).Normalize();
			Transform transform = Transform.Identity;
			transform.Origin = origin;
			transform.BasisX = zNormal.CrossProduct(XYZ.BasisZ).Normalize();
			transform.BasisY = XYZ.BasisZ;
			transform.BasisZ = zNormal;

			return transform;
		}


		/// <summary>
		/// 在点origin处绕向量normal逆时针旋转angle度
		/// </summary>
		/// <param name="origin"></param>
		/// <param name="normal"></param>
		/// <param name="angle"></param>
		/// <returns></returns>
		public static Transform GetTransform(XYZ origin, XYZ normal, double angle)
		{
			Transform transform = Transform.CreateTranslation(origin);
			Transform rotation = Transform.CreateRotation(-normal, angle);
			return transform.Multiply(rotation);
		}
		/// <summary>
		/// 获取Curveloop按距离等分后的所有垂直于曲线的变换
		/// </summary>
		/// <param name="loop"></param>
		/// <param name="distance"></param>
		/// <returns></returns>
		public static List<Transform> GetTransformsV(CurveLoop loop, double distance)
		{
			List<Transform> transforms = new List<Transform>();
			int count = (int)Math.Floor(loop.GetExactLength() / distance);
			for (int i = 0; i < count; i++)
			{
				double para = i * distance / loop.GetExactLength();//返回循环中所有曲线的精确长度之和。 
				double endpara = 0;
				foreach (var item in loop)
				{
					double startpara = endpara;
					endpara += item.Length / loop.GetExactLength();
					if (para > startpara && para <= endpara)
					{
						double expara = (para - startpara) / (endpara - startpara);
						transforms.Add(GetTransformVertical(item, expara));
					}
				}
			}
			return transforms;
		}
		/// <summary>
		/// 获取Curveloop按距离等分后的所有平行于曲线的变换
		/// </summary>
		/// <param name="loop"></param>
		/// <param name="distance"></param>
		/// <returns></returns>
		public static List<Transform> GetTransformsP(CurveLoop loop, double distance)
		{
			List<Transform> transforms = new List<Transform>();
			int count = (int)Math.Floor(loop.GetExactLength() / distance);
			for (int i = 0; i < count; i++)
			{
				double para = i * distance / loop.GetExactLength();
				double endpara = 0;
				foreach (var item in loop)
				{
					double startpara = endpara;
					endpara += item.Length / loop.GetExactLength();
					if (para > startpara && para <= endpara)
					{
						double expara = (para - startpara) / (endpara - startpara);
						transforms.Add(GetTransformParallel(item, expara));
					}
				}
			}
			return transforms;
		}

        /// <summary>
        /// 创建模型线
        /// </summary>
        /// <param name="document"></param>
        /// <param name="curve"></param>
        public static void CreateModelCurve(Document document, Curve curve)
        {
            Plane plane;
            if (curve is Line)
            {
                Line line = curve as Line;
                if (line.Direction.IsAlmostEqualTo(XYZ.BasisZ))
                {
                    plane = Plane.CreateByThreePoints(curve.GetEndPoint(0), curve.GetEndPoint(1), curve.GetEndPoint(1) + XYZ.BasisX);
                }
                else
                {
                    plane = Plane.CreateByThreePoints(curve.GetEndPoint(0), curve.GetEndPoint(1), curve.GetEndPoint(1) + XYZ.BasisZ);
                }
            }
            else
            {
                plane = Plane.CreateByThreePoints(curve.GetEndPoint(0), curve.GetEndPoint(1), curve.Evaluate(0.5, true));
            }
            document.Create.NewModelCurve(curve, SketchPlane.Create(document, plane));
        }

    }
}
